Sunburst#

Hide imports
from dimcat.steps import slicers, groupers
%load_ext autoreload
%autoreload 2
import os
import ms3
import pandas as pd
from dimcat import plotting, Pipeline

import utils
import plotly.io as pio
# workaround to remove the "loading mathjax" box from the PDF figure
# see https://github.com/plotly/plotly.py/issues/3469#issuecomment-994907721
pio.kaleido.scope.mathjax = None
# if mathjax was needed to render math, one could try
# pio.full_figure_for_development(fig, warn=False)

pd.set_option('display.max_rows', 500)
pd.set_option('display.max_columns', 100)
SUNBURST_WIDTH = 1620
TERMINAL_SYMBOL = "∎"
RESULTS_PATH = os.path.abspath(os.path.join(utils.OUTPUT_FOLDER, "scale_degrees"))
os.makedirs(RESULTS_PATH, exist_ok=True)


def make_output_path(
    filename: str,
    extension=None,
    path=RESULTS_PATH,
) -> str:
    return utils.make_output_path(filename=filename, extension=extension, path=path)


def save_figure_as(
    fig, filename, formats=("png", "pdf"), directory=RESULTS_PATH, **kwargs
):
    if formats is not None:
        for fmt in formats:
            plotting.write_image(fig, filename, directory, format=fmt, **kwargs)
    else:
        plotting.write_image(fig, filename, directory, **kwargs)

Loading data

D = utils.get_dataset("couperin_concerts", corpus_release="v2.2")
D
Dataset
=======
{'inputs': {'basepath': None,
            'packages': {'couperin_concerts': ["'couperin_concerts.measures' (MuseScoreMeasures)",
                                               "'couperin_concerts.notes' (MuseScoreNotes)",
                                               "'couperin_concerts.expanded' (MuseScoreHarmonies)",
                                               "'couperin_concerts.chords' (MuseScoreChords)",
                                               "'couperin_concerts.metadata' (Metadata)"]}},
 'outputs': {'basepath': None, 'packages': {}},
 'pipeline': []}

Key areas#

sliced_D = slicers.KeySlicer().process(D)
grouped_D = groupers.ModeGrouper().process(sliced_D)
grouped_D
SlicedGroupedDataset
====================
{'inputs': {'basepath': None,
            'packages': {'couperin_concerts': ["'couperin_concerts.measures' (MuseScoreMeasures)",
                                               "'couperin_concerts.notes' (MuseScoreNotes)",
                                               "'couperin_concerts.expanded' (MuseScoreHarmonies)",
                                               "'couperin_concerts.chords' (MuseScoreChords)",
                                               "'couperin_concerts.metadata' (Metadata)"]}},
 'outputs': {'basepath': None,
             'packages': {'features': ["'couperin_concerts.expanded.keyannotations' (KeyAnnotations)"]}},
 'pipeline': ['FeatureExtractor', 'KeySlicer', 'ModeGrouper']}
notes = sliced_D.get_feature('notes')
notes
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	239 rows are coming without without time spans:
[('couperin_concerts', 'c01n03_sarabande', 63), ('couperin_concerts', 'c01n03_sarabande', 64), ('couperin_concerts', 'c01n03_sarabande', 65), ('couperin_concerts', 'c01n03_sarabande', 66), ('couperin_concerts', 'c01n03_sarabande', 67), ('couperin_concerts', 'c01n03_sarabande', 68), ('couperin_concerts', 'c01n03_sarabande', 69), ('couperin_concerts', 'c01n03_sarabande', 70), ('couperin_concerts', 'c01n03_sarabande', 279), ('couperin_concerts', 'c01n03_sarabande', 280), ('couperin_concerts', 'c01n03_sarabande', 281), ('couperin_concerts', 'c01n03_sarabande', 282), ('couperin_concerts', 'c01n03_sarabande', 283), ('couperin_concerts', 'c01n03_sarabande', 284), ('couperin_concerts', 'c01n03_sarabande', 285), ('couperin_concerts', 'c01n03_sarabande', 286), ('couperin_concerts', 'c01n04_gavotte', 46), ('couperin_concerts', 'c01n04_gavotte', 47), ('couperin_concerts', 'c01n04_gavotte', 48), ('couperin_concerts', 'c01n04_gavotte', 49), ('couperin_concerts', 'c01n04_gavotte', 50), ('couperin_concerts', 'c01n04_gavotte', 51), ('couperin_concerts', 'c01n04_gavotte', 185), ('couperin_concerts', 'c01n04_gavotte', 186), ('couperin_concerts', 'c01n04_gavotte', 187), ('couperin_concerts', 'c01n04_gavotte', 188), ('couperin_concerts', 'c01n04_gavotte', 189), ('couperin_concerts', 'c01n04_gavotte', 190), ('couperin_concerts', 'c01n05_gigue', 126), ('couperin_concerts', 'c01n05_gigue', 127), ('couperin_concerts', 'c01n05_gigue', 128), ('couperin_concerts', 'c01n05_gigue', 129), ('couperin_concerts', 'c01n05_gigue', 377), ('couperin_concerts', 'c01n05_gigue', 378), ('couperin_concerts', 'c01n05_gigue', 379), ('couperin_concerts', 'c01n05_gigue', 380), ('couperin_concerts', 'c01n05_gigue', 381), ('couperin_concerts', 'c01n05_gigue', 382), ('couperin_concerts', 'c01n05_gigue', 383), ('couperin_concerts', 'c01n05_gigue', 384), ('couperin_concerts', 'c01n05_gigue', 385), ('couperin_concerts', 'c01n05_gigue', 386), ('couperin_concerts', 'c01n05_gigue', 387), ('couperin_concerts', 'c01n06_menuet_en_trio', 76), ('couperin_concerts', 'c01n06_menuet_en_trio', 77), ('couperin_concerts', 'c01n06_menuet_en_trio', 78), ('couperin_concerts', 'c01n06_menuet_en_trio', 79), ('couperin_concerts', 'c01n06_menuet_en_trio', 80), ('couperin_concerts', 'c01n06_menuet_en_trio', 81), ('couperin_concerts', 'c01n06_menuet_en_trio', 82), ('couperin_concerts', 'c01n06_menuet_en_trio', 83), ('couperin_concerts', 'c01n06_menuet_en_trio', 241), ('couperin_concerts', 'c01n06_menuet_en_trio', 242), ('couperin_concerts', 'c01n06_menuet_en_trio', 243), ('couperin_concerts', 'c01n06_menuet_en_trio', 244), ('couperin_concerts', 'c01n06_menuet_en_trio', 245), ('couperin_concerts', 'c01n06_menuet_en_trio', 246), ('couperin_concerts', 'c01n06_menuet_en_trio', 247), ('couperin_concerts', 'c01n06_menuet_en_trio', 248), ('couperin_concerts', 'c02n04_air_contrefugue', 671), ('couperin_concerts', 'c02n04_air_contrefugue', 672), ('couperin_concerts', 'c02n04_air_contrefugue', 673), ('couperin_concerts', 'c02n04_air_contrefugue', 674), ('couperin_concerts', 'c03n03_courante', 156), ('couperin_concerts', 'c03n03_courante', 157), ('couperin_concerts', 'c03n03_courante', 158), ('couperin_concerts', 'c03n03_courante', 159), ('couperin_concerts', 'c03n03_courante', 160), ('couperin_concerts', 'c03n03_courante', 161), ('couperin_concerts', 'c03n03_courante', 162), ('couperin_concerts', 'c03n03_courante', 163), ('couperin_concerts', 'c03n03_courante', 164), ('couperin_concerts', 'c03n03_courante', 165), ('couperin_concerts', 'c03n03_courante', 166), ('couperin_concerts', 'c03n03_courante', 167), ('couperin_concerts', 'c03n03_courante', 168), ('couperin_concerts', 'c03n03_courante', 169), ('couperin_concerts', 'c03n03_courante', 170), ('couperin_concerts', 'c03n03_courante', 171), ('couperin_concerts', 'c03n03_courante', 172), ('couperin_concerts', 'c03n03_courante', 173), ('couperin_concerts', 'c03n03_courante', 174), ('couperin_concerts', 'c03n03_courante', 365), ('couperin_concerts', 'c03n03_courante', 366), ('couperin_concerts', 'c03n03_courante', 367), ('couperin_concerts', 'c03n03_courante', 368), ('couperin_concerts', 'c03n03_courante', 369), ('couperin_concerts', 'c03n03_courante', 370), ('couperin_concerts', 'c03n03_courante', 371), ('couperin_concerts', 'c03n03_courante', 372), ('couperin_concerts', 'c03n03_courante', 373), ('couperin_concerts', 'c03n03_courante', 374), ('couperin_concerts', 'c03n03_courante', 375), ('couperin_concerts', 'c03n03_courante', 376), ('couperin_concerts', 'c03n03_courante', 377), ('couperin_concerts', 'c03n03_courante', 378), ('couperin_concerts', 'c03n03_courante', 379), ('couperin_concerts', 'c03n03_courante', 380), ('couperin_concerts', 'c03n03_courante', 381), ('couperin_concerts', 'c03n03_courante', 382), ('couperin_concerts', 'c03n03_courante', 383), ('couperin_concerts', 'c04n04_courante_a_litalienne', 725), ('couperin_concerts', 'c04n04_courante_a_litalienne', 726), ('couperin_concerts', 'c04n04_courante_a_litalienne', 727), ('couperin_concerts', 'c04n04_courante_a_litalienne', 728), ('couperin_concerts', 'c05n02_allemande', 132), ('couperin_concerts', 'c05n02_allemande', 133), ('couperin_concerts', 'c05n02_allemande', 578), ('couperin_concerts', 'c05n02_allemande', 579), ('couperin_concerts', 'c08n01_ouverture', 152), ('couperin_concerts', 'c08n01_ouverture', 153), ('couperin_concerts', 'c08n01_ouverture', 154), ('couperin_concerts', 'c08n01_ouverture', 155), ('couperin_concerts', 'c08n01_ouverture', 156), ('couperin_concerts', 'c08n01_ouverture', 157), ('couperin_concerts', 'c08n01_ouverture', 158), ('couperin_concerts', 'c08n01_ouverture', 159), ('couperin_concerts', 'c08n01_ouverture', 160), ('couperin_concerts', 'c08n01_ouverture', 161), ('couperin_concerts', 'c08n01_ouverture', 162), ('couperin_concerts', 'c08n03_air', 97), ('couperin_concerts', 'c08n03_air', 98), ('couperin_concerts', 'c08n03_air', 289), ('couperin_concerts', 'c08n03_air', 290), ('couperin_concerts', 'c08n03_air', 291), ('couperin_concerts', 'c08n03_air', 292), ('couperin_concerts', 'c08n03_air', 293), ('couperin_concerts', 'c08n03_air', 294), ('couperin_concerts', 'c08n03_air', 295), ('couperin_concerts', 'c08n05_air_leger', 192), ('couperin_concerts', 'c08n05_air_leger', 193), ('couperin_concerts', 'c08n05_air_leger', 194), ('couperin_concerts', 'c08n08_sarabande', 39), ('couperin_concerts', 'c08n08_sarabande', 40), ('couperin_concerts', 'c08n08_sarabande', 41), ('couperin_concerts', 'c08n08_sarabande', 42), ('couperin_concerts', 'c08n08_sarabande', 43), ('couperin_concerts', 'c08n08_sarabande', 44), ('couperin_concerts', 'c08n08_sarabande', 45), ('couperin_concerts', 'c08n08_sarabande', 143), ('couperin_concerts', 'c08n08_sarabande', 144), ('couperin_concerts', 'c08n08_sarabande', 145), ('couperin_concerts', 'c08n08_sarabande', 146), ('couperin_concerts', 'c08n08_sarabande', 147), ('couperin_concerts', 'c08n08_sarabande', 148), ('couperin_concerts', 'c08n09_air_leger', 75), ('couperin_concerts', 'c08n09_air_leger', 76), ('couperin_concerts', 'c08n09_air_leger', 77), ('couperin_concerts', 'c08n09_air_leger', 78), ('couperin_concerts', 'c08n09_air_leger', 79), ('couperin_concerts', 'c08n09_air_leger', 80), ('couperin_concerts', 'c08n09_air_leger', 81), ('couperin_concerts', 'c08n10_air_lentement', 133), ('couperin_concerts', 'c08n10_air_lentement', 134), ('couperin_concerts', 'c08n10_air_lentement', 135), ('couperin_concerts', 'c08n10_air_lentement', 136), ('couperin_concerts', 'c08n10_air_lentement', 137), ('couperin_concerts', 'c08n10_air_lentement', 138), ('couperin_concerts', 'c08n10_air_lentement', 139), ('couperin_concerts', 'c08n10_air_lentement', 360), ('couperin_concerts', 'c08n10_air_lentement', 361), ('couperin_concerts', 'c08n10_air_lentement', 362), ('couperin_concerts', 'c08n10_air_lentement', 363), ('couperin_concerts', 'c08n10_air_lentement', 364), ('couperin_concerts', 'c08n10_air_lentement', 365), ('couperin_concerts', 'c08n10_air_lentement', 366), ('couperin_concerts', 'c08n10_air_lentement', 367), ('couperin_concerts', 'c08n10_air_lentement', 368), ('couperin_concerts', 'c08n10_air_lentement', 369), ('couperin_concerts', 'c08n10_air_lentement', 370), ('couperin_concerts', 'c09n02_lenjouement', 616), ('couperin_concerts', 'c09n02_lenjouement', 617), ('couperin_concerts', 'c09n06_Sarabande', 62), ('couperin_concerts', 'c09n06_Sarabande', 63), ('couperin_concerts', 'c09n06_Sarabande', 64), ('couperin_concerts', 'c09n06_Sarabande', 65), ('couperin_concerts', 'c09n06_Sarabande', 66), ('couperin_concerts', 'c09n06_Sarabande', 67), ('couperin_concerts', 'c09n06_Sarabande', 68), ('couperin_concerts', 'c09n07_douceur', 147), ('couperin_concerts', 'c09n07_douceur', 148), ('couperin_concerts', 'c09n07_douceur', 332), ('couperin_concerts', 'c09n07_douceur', 333), ('couperin_concerts', 'c10n03_plainte', 106), ('couperin_concerts', 'c10n03_plainte', 107), ('couperin_concerts', 'c10n03_plainte', 108), ('couperin_concerts', 'c10n03_plainte', 109), ('couperin_concerts', 'c10n03_plainte', 110), ('couperin_concerts', 'c10n03_plainte', 111), ('couperin_concerts', 'c10n03_plainte', 112), ('couperin_concerts', 'c10n03_plainte', 113), ('couperin_concerts', 'c10n03_plainte', 114), ('couperin_concerts', 'c10n03_plainte', 115), ('couperin_concerts', 'c10n03_plainte', 116), ('couperin_concerts', 'c10n03_plainte', 117), ('couperin_concerts', 'c10n03_plainte', 118), ('couperin_concerts', 'c10n03_plainte', 119), ('couperin_concerts', 'c10n03_plainte', 120), ('couperin_concerts', 'c10n03_plainte', 121), ('couperin_concerts', 'c10n03_plainte', 122), ('couperin_concerts', 'c10n03_plainte', 123), ('couperin_concerts', 'c10n03_plainte', 291), ('couperin_concerts', 'c10n03_plainte', 292), ('couperin_concerts', 'c10n03_plainte', 293), ('couperin_concerts', 'c10n03_plainte', 294), ('couperin_concerts', 'c10n03_plainte', 295), ('couperin_concerts', 'c10n03_plainte', 296), ('couperin_concerts', 'c10n03_plainte', 297), ('couperin_concerts', 'c10n03_plainte', 298), ('couperin_concerts', 'c10n03_plainte', 299), ('couperin_concerts', 'c10n03_plainte', 300), ('couperin_concerts', 'c10n03_plainte', 301), ('couperin_concerts', 'c10n03_plainte', 302), ('couperin_concerts', 'c10n03_plainte', 303), ('couperin_concerts', 'c10n03_plainte', 304), ('couperin_concerts', 'c10n03_plainte', 305), ('couperin_concerts', 'c10n03_plainte', 547), ('couperin_concerts', 'c10n03_plainte', 548), ('couperin_concerts', 'c10n03_plainte', 549), ('couperin_concerts', 'c11n04_courante', 215), ('couperin_concerts', 'c11n04_courante', 216), ('couperin_concerts', 'c11n04_courante', 217), ('couperin_concerts', 'c11n04_courante', 218), ('couperin_concerts', 'c11n07_gigue', 131), ('couperin_concerts', 'c11n07_gigue', 132), ('couperin_concerts', 'c11n07_gigue', 456), ('couperin_concerts', 'c11n07_gigue', 457), ('couperin_concerts', 'c11n07_gigue', 458), ('couperin_concerts', 'c11n07_gigue', 459), ('couperin_concerts', 'c11n07_gigue', 460), ('couperin_concerts', 'c11n07_gigue', 461), ('couperin_concerts', 'c11n07_gigue', 462), ('couperin_concerts', 'c11n07_gigue', 463), ('couperin_concerts', 'c11n07_gigue', 464), ('couperin_concerts', 'c11n07_gigue', 465), ('couperin_concerts', 'c13n03_sarabande', 59), ('couperin_concerts', 'c13n03_sarabande', 60), ('couperin_concerts', 'c14n02_allemande', 359), ('couperin_concerts', 'c14n02_allemande', 360)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	16 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c01n03_sarabande', 63), ('couperin_concerts', 'c01n03_sarabande', 64), ('couperin_concerts', 'c01n03_sarabande', 65), ('couperin_concerts', 'c01n03_sarabande', 66), ('couperin_concerts', 'c01n03_sarabande', 67), ('couperin_concerts', 'c01n03_sarabande', 68), ('couperin_concerts', 'c01n03_sarabande', 69), ('couperin_concerts', 'c01n03_sarabande', 70), ('couperin_concerts', 'c01n03_sarabande', 279), ('couperin_concerts', 'c01n03_sarabande', 280), ('couperin_concerts', 'c01n03_sarabande', 281), ('couperin_concerts', 'c01n03_sarabande', 282), ('couperin_concerts', 'c01n03_sarabande', 283), ('couperin_concerts', 'c01n03_sarabande', 284), ('couperin_concerts', 'c01n03_sarabande', 285), ('couperin_concerts', 'c01n03_sarabande', 286)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	12 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c01n04_gavotte', 46), ('couperin_concerts', 'c01n04_gavotte', 47), ('couperin_concerts', 'c01n04_gavotte', 48), ('couperin_concerts', 'c01n04_gavotte', 49), ('couperin_concerts', 'c01n04_gavotte', 50), ('couperin_concerts', 'c01n04_gavotte', 51), ('couperin_concerts', 'c01n04_gavotte', 185), ('couperin_concerts', 'c01n04_gavotte', 186), ('couperin_concerts', 'c01n04_gavotte', 187), ('couperin_concerts', 'c01n04_gavotte', 188), ('couperin_concerts', 'c01n04_gavotte', 189), ('couperin_concerts', 'c01n04_gavotte', 190)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	15 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c01n05_gigue', 126), ('couperin_concerts', 'c01n05_gigue', 127), ('couperin_concerts', 'c01n05_gigue', 128), ('couperin_concerts', 'c01n05_gigue', 129), ('couperin_concerts', 'c01n05_gigue', 377), ('couperin_concerts', 'c01n05_gigue', 378), ('couperin_concerts', 'c01n05_gigue', 379), ('couperin_concerts', 'c01n05_gigue', 380), ('couperin_concerts', 'c01n05_gigue', 381), ('couperin_concerts', 'c01n05_gigue', 382), ('couperin_concerts', 'c01n05_gigue', 383), ('couperin_concerts', 'c01n05_gigue', 384), ('couperin_concerts', 'c01n05_gigue', 385), ('couperin_concerts', 'c01n05_gigue', 386), ('couperin_concerts', 'c01n05_gigue', 387)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	16 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c01n06_menuet_en_trio', 76), ('couperin_concerts', 'c01n06_menuet_en_trio', 77), ('couperin_concerts', 'c01n06_menuet_en_trio', 78), ('couperin_concerts', 'c01n06_menuet_en_trio', 79), ('couperin_concerts', 'c01n06_menuet_en_trio', 80), ('couperin_concerts', 'c01n06_menuet_en_trio', 81), ('couperin_concerts', 'c01n06_menuet_en_trio', 82), ('couperin_concerts', 'c01n06_menuet_en_trio', 83), ('couperin_concerts', 'c01n06_menuet_en_trio', 241), ('couperin_concerts', 'c01n06_menuet_en_trio', 242), ('couperin_concerts', 'c01n06_menuet_en_trio', 243), ('couperin_concerts', 'c01n06_menuet_en_trio', 244), ('couperin_concerts', 'c01n06_menuet_en_trio', 245), ('couperin_concerts', 'c01n06_menuet_en_trio', 246), ('couperin_concerts', 'c01n06_menuet_en_trio', 247), ('couperin_concerts', 'c01n06_menuet_en_trio', 248)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	4 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c02n04_air_contrefugue', 671), ('couperin_concerts', 'c02n04_air_contrefugue', 672), ('couperin_concerts', 'c02n04_air_contrefugue', 673), ('couperin_concerts', 'c02n04_air_contrefugue', 674)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	38 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c03n03_courante', 156), ('couperin_concerts', 'c03n03_courante', 157), ('couperin_concerts', 'c03n03_courante', 158), ('couperin_concerts', 'c03n03_courante', 159), ('couperin_concerts', 'c03n03_courante', 160), ('couperin_concerts', 'c03n03_courante', 161), ('couperin_concerts', 'c03n03_courante', 162), ('couperin_concerts', 'c03n03_courante', 163), ('couperin_concerts', 'c03n03_courante', 164), ('couperin_concerts', 'c03n03_courante', 165), ('couperin_concerts', 'c03n03_courante', 166), ('couperin_concerts', 'c03n03_courante', 167), ('couperin_concerts', 'c03n03_courante', 168), ('couperin_concerts', 'c03n03_courante', 169), ('couperin_concerts', 'c03n03_courante', 170), ('couperin_concerts', 'c03n03_courante', 171), ('couperin_concerts', 'c03n03_courante', 172), ('couperin_concerts', 'c03n03_courante', 173), ('couperin_concerts', 'c03n03_courante', 174), ('couperin_concerts', 'c03n03_courante', 365), ('couperin_concerts', 'c03n03_courante', 366), ('couperin_concerts', 'c03n03_courante', 367), ('couperin_concerts', 'c03n03_courante', 368), ('couperin_concerts', 'c03n03_courante', 369), ('couperin_concerts', 'c03n03_courante', 370), ('couperin_concerts', 'c03n03_courante', 371), ('couperin_concerts', 'c03n03_courante', 372), ('couperin_concerts', 'c03n03_courante', 373), ('couperin_concerts', 'c03n03_courante', 374), ('couperin_concerts', 'c03n03_courante', 375), ('couperin_concerts', 'c03n03_courante', 376), ('couperin_concerts', 'c03n03_courante', 377), ('couperin_concerts', 'c03n03_courante', 378), ('couperin_concerts', 'c03n03_courante', 379), ('couperin_concerts', 'c03n03_courante', 380), ('couperin_concerts', 'c03n03_courante', 381), ('couperin_concerts', 'c03n03_courante', 382), ('couperin_concerts', 'c03n03_courante', 383)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	4 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c04n04_courante_a_litalienne', 725), ('couperin_concerts', 'c04n04_courante_a_litalienne', 726), ('couperin_concerts', 'c04n04_courante_a_litalienne', 727), ('couperin_concerts', 'c04n04_courante_a_litalienne', 728)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	4 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c05n02_allemande', 132), ('couperin_concerts', 'c05n02_allemande', 133), ('couperin_concerts', 'c05n02_allemande', 578), ('couperin_concerts', 'c05n02_allemande', 579)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	11 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c08n01_ouverture', 152), ('couperin_concerts', 'c08n01_ouverture', 153), ('couperin_concerts', 'c08n01_ouverture', 154), ('couperin_concerts', 'c08n01_ouverture', 155), ('couperin_concerts', 'c08n01_ouverture', 156), ('couperin_concerts', 'c08n01_ouverture', 157), ('couperin_concerts', 'c08n01_ouverture', 158), ('couperin_concerts', 'c08n01_ouverture', 159), ('couperin_concerts', 'c08n01_ouverture', 160), ('couperin_concerts', 'c08n01_ouverture', 161), ('couperin_concerts', 'c08n01_ouverture', 162)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	9 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c08n03_air', 97), ('couperin_concerts', 'c08n03_air', 98), ('couperin_concerts', 'c08n03_air', 289), ('couperin_concerts', 'c08n03_air', 290), ('couperin_concerts', 'c08n03_air', 291), ('couperin_concerts', 'c08n03_air', 292), ('couperin_concerts', 'c08n03_air', 293), ('couperin_concerts', 'c08n03_air', 294), ('couperin_concerts', 'c08n03_air', 295)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	3 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c08n05_air_leger', 192), ('couperin_concerts', 'c08n05_air_leger', 193), ('couperin_concerts', 'c08n05_air_leger', 194)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	13 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c08n08_sarabande', 39), ('couperin_concerts', 'c08n08_sarabande', 40), ('couperin_concerts', 'c08n08_sarabande', 41), ('couperin_concerts', 'c08n08_sarabande', 42), ('couperin_concerts', 'c08n08_sarabande', 43), ('couperin_concerts', 'c08n08_sarabande', 44), ('couperin_concerts', 'c08n08_sarabande', 45), ('couperin_concerts', 'c08n08_sarabande', 143), ('couperin_concerts', 'c08n08_sarabande', 144), ('couperin_concerts', 'c08n08_sarabande', 145), ('couperin_concerts', 'c08n08_sarabande', 146), ('couperin_concerts', 'c08n08_sarabande', 147), ('couperin_concerts', 'c08n08_sarabande', 148)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	7 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c08n09_air_leger', 75), ('couperin_concerts', 'c08n09_air_leger', 76), ('couperin_concerts', 'c08n09_air_leger', 77), ('couperin_concerts', 'c08n09_air_leger', 78), ('couperin_concerts', 'c08n09_air_leger', 79), ('couperin_concerts', 'c08n09_air_leger', 80), ('couperin_concerts', 'c08n09_air_leger', 81)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	18 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c08n10_air_lentement', 133), ('couperin_concerts', 'c08n10_air_lentement', 134), ('couperin_concerts', 'c08n10_air_lentement', 135), ('couperin_concerts', 'c08n10_air_lentement', 136), ('couperin_concerts', 'c08n10_air_lentement', 137), ('couperin_concerts', 'c08n10_air_lentement', 138), ('couperin_concerts', 'c08n10_air_lentement', 139), ('couperin_concerts', 'c08n10_air_lentement', 360), ('couperin_concerts', 'c08n10_air_lentement', 361), ('couperin_concerts', 'c08n10_air_lentement', 362), ('couperin_concerts', 'c08n10_air_lentement', 363), ('couperin_concerts', 'c08n10_air_lentement', 364), ('couperin_concerts', 'c08n10_air_lentement', 365), ('couperin_concerts', 'c08n10_air_lentement', 366), ('couperin_concerts', 'c08n10_air_lentement', 367), ('couperin_concerts', 'c08n10_air_lentement', 368), ('couperin_concerts', 'c08n10_air_lentement', 369), ('couperin_concerts', 'c08n10_air_lentement', 370)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	2 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c09n02_lenjouement', 616), ('couperin_concerts', 'c09n02_lenjouement', 617)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	7 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c09n06_Sarabande', 62), ('couperin_concerts', 'c09n06_Sarabande', 63), ('couperin_concerts', 'c09n06_Sarabande', 64), ('couperin_concerts', 'c09n06_Sarabande', 65), ('couperin_concerts', 'c09n06_Sarabande', 66), ('couperin_concerts', 'c09n06_Sarabande', 67), ('couperin_concerts', 'c09n06_Sarabande', 68)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	4 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c09n07_douceur', 147), ('couperin_concerts', 'c09n07_douceur', 148), ('couperin_concerts', 'c09n07_douceur', 332), ('couperin_concerts', 'c09n07_douceur', 333)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	36 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c10n03_plainte', 106), ('couperin_concerts', 'c10n03_plainte', 107), ('couperin_concerts', 'c10n03_plainte', 108), ('couperin_concerts', 'c10n03_plainte', 109), ('couperin_concerts', 'c10n03_plainte', 110), ('couperin_concerts', 'c10n03_plainte', 111), ('couperin_concerts', 'c10n03_plainte', 112), ('couperin_concerts', 'c10n03_plainte', 113), ('couperin_concerts', 'c10n03_plainte', 114), ('couperin_concerts', 'c10n03_plainte', 115), ('couperin_concerts', 'c10n03_plainte', 116), ('couperin_concerts', 'c10n03_plainte', 117), ('couperin_concerts', 'c10n03_plainte', 118), ('couperin_concerts', 'c10n03_plainte', 119), ('couperin_concerts', 'c10n03_plainte', 120), ('couperin_concerts', 'c10n03_plainte', 121), ('couperin_concerts', 'c10n03_plainte', 122), ('couperin_concerts', 'c10n03_plainte', 123), ('couperin_concerts', 'c10n03_plainte', 291), ('couperin_concerts', 'c10n03_plainte', 292), ('couperin_concerts', 'c10n03_plainte', 293), ('couperin_concerts', 'c10n03_plainte', 294), ('couperin_concerts', 'c10n03_plainte', 295), ('couperin_concerts', 'c10n03_plainte', 296), ('couperin_concerts', 'c10n03_plainte', 297), ('couperin_concerts', 'c10n03_plainte', 298), ('couperin_concerts', 'c10n03_plainte', 299), ('couperin_concerts', 'c10n03_plainte', 300), ('couperin_concerts', 'c10n03_plainte', 301), ('couperin_concerts', 'c10n03_plainte', 302), ('couperin_concerts', 'c10n03_plainte', 303), ('couperin_concerts', 'c10n03_plainte', 304), ('couperin_concerts', 'c10n03_plainte', 305), ('couperin_concerts', 'c10n03_plainte', 547), ('couperin_concerts', 'c10n03_plainte', 548), ('couperin_concerts', 'c10n03_plainte', 549)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	4 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c11n04_courante', 215), ('couperin_concerts', 'c11n04_courante', 216), ('couperin_concerts', 'c11n04_courante', 217), ('couperin_concerts', 'c11n04_courante', 218)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	12 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c11n07_gigue', 131), ('couperin_concerts', 'c11n07_gigue', 132), ('couperin_concerts', 'c11n07_gigue', 456), ('couperin_concerts', 'c11n07_gigue', 457), ('couperin_concerts', 'c11n07_gigue', 458), ('couperin_concerts', 'c11n07_gigue', 459), ('couperin_concerts', 'c11n07_gigue', 460), ('couperin_concerts', 'c11n07_gigue', 461), ('couperin_concerts', 'c11n07_gigue', 462), ('couperin_concerts', 'c11n07_gigue', 463), ('couperin_concerts', 'c11n07_gigue', 464), ('couperin_concerts', 'c11n07_gigue', 465)]
WARNING  dimcat.data.resources.features.Notes -- /home/laser/git/dimcat/src/dimcat/data/resources/utils.py (line 634) in get_time_spans_from_resource_df():
	2 rows are coming without without time spans and have been dropped:
[('couperin_concerts', 'c14n02_allemande', 359), ('couperin_concerts', 'c14n02_allemande', 360)]
mc mn quarterbeats quarterbeats_all_endings duration_qb duration mc_onset mn_onset timesig staff voice volta chord_id gracenote midi name nominal_duration octave scalar tied tpc_name tpc
corpus piece localkey_slice i
couperin_concerts c01n01_prelude [0.0, 16.0) 0 1 0 0 0 0.50 1/8 0 1/2 4/4 2 1 <NA> 3 <NA> 43 G2 1/8 2 1 <NA> G 1
1 1 0 0 0 1.00 1/4 0 1/2 4/4 1 1 <NA> 0 <NA> 74 D5 1/4 5 1 1 D 2
2 1 0 1/2 1/2 0.50 1/8 1/8 5/8 4/4 2 1 <NA> 4 <NA> 45 A2 1/8 2 1 <NA> A 3
3 1 0 1 1 0.50 1/8 1/4 3/4 4/4 2 1 <NA> 5 <NA> 47 B2 1/8 2 1 <NA> B 5
4 1 0 1 1 0.75 3/16 1/4 3/4 4/4 1 1 <NA> 1 <NA> 74 D5 1/8 5 3/2 -1 D 2
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
parnasse_07 [173.0, 212.0) 1278 52 52 831/4 831/4 0.25 1/16 15/16 15/16 4/4 1 1 <NA> 1260 <NA> 71 B4 1/16 4 1 <NA> B 5
1279 52 52 831/4 831/4 0.25 1/16 15/16 15/16 4/4 2 1 <NA> 1272 <NA> 71 B4 1/16 4 1 <NA> B 5
1280 53 53 208 208 4.00 1 0 0 4/4 3 1 <NA> 1282 <NA> 47 B2 1 2 1 <NA> B 5
1281 53 53 208 208 4.00 1 0 0 4/4 1 1 <NA> 1280 <NA> 71 B4 1 4 1 <NA> B 5
1282 53 53 208 208 4.00 1 0 0 4/4 2 1 <NA> 1281 <NA> 71 B4 1 4 1 <NA> B 5

35264 rows × 22 columns

result = notes.get_default_analysis()
result
duration_qb
corpus piece localkey_slice tpc tpc_name
couperin_concerts c01n01_prelude [0.0, 16.0) -1 F 1.000000
0 C 1.750000
1 G 7.583333
2 D 8.000000
3 A 3.291667
... ... ... ... ...
parnasse_07 [173.0, 212.0) 6 F# 18.500000
7 C# 9.750000
8 G# 3.000000
9 D# 3.000000
10 A# 5.250000

4463 rows × 1 columns

result.plot_grouped()
notes.head()
mc mn quarterbeats quarterbeats_all_endings duration_qb duration mc_onset mn_onset timesig staff voice volta chord_id gracenote midi name nominal_duration octave scalar tied tpc_name tpc
corpus piece localkey_slice i
couperin_concerts c01n01_prelude [0.0, 16.0) 0 1 0 0 0 0.50 1/8 0 1/2 4/4 2 1 <NA> 3 <NA> 43 G2 1/8 2 1 <NA> G 1
1 1 0 0 0 1.00 1/4 0 1/2 4/4 1 1 <NA> 0 <NA> 74 D5 1/4 5 1 1 D 2
2 1 0 1/2 1/2 0.50 1/8 1/8 5/8 4/4 2 1 <NA> 4 <NA> 45 A2 1/8 2 1 <NA> A 3
3 1 0 1 1 0.50 1/8 1/4 3/4 4/4 2 1 <NA> 5 <NA> 47 B2 1/8 2 1 <NA> B 5
4 1 0 1 1 0.75 3/16 1/4 3/4 4/4 1 1 <NA> 1 <NA> 74 D5 1/8 5 3/2 -1 D 2
keys = grouped_D.get_feature("KeyAnnotations")
print(f"Overall number of key segments is {len(keys.index)}")
keys.head()
Overall number of key segments is 566
mc mn quarterbeats duration_qb mc_onset mn_onset timesig staff voice volta label globalkey_is_minor localkey_is_minor globalkey_mode localkey_mode localkey_resolved localkey_and_mode globalkey localkey
mode corpus piece localkey_slice i
major couperin_concerts c01n01_prelude [0.0, 16.0) 0 1 0 0 16.0 0 1/2 4/4 1 1 <NA> G.I{ False False major major I I, major G I
[22.5, 32.0) 22 7 6 45/2 9.5 1/8 1/8 4/4 1 1 <NA> V.V{ False False major major V V, major G V
[32.0, 40.0) 35 9 8 32 8.0 1/2 1/2 4/4 1 1 <NA> IV.ii6{ False False major major IV IV, major G IV
[40.0, 47.0) 41 11 9 40 7.0 1/2 1/2 4/4 1 1 <NA> V.V{ False False major major V V, major G V
[47.0, 98.0) 48 13 11 47 51.0 1/4 1/4 4/4 1 1 <NA> I.V65 False False major major I I, major G I
# this bit is copied from the annotations notebook
keys_data = keys[[col for col in keys.columns if col not in notes.columns]].droplevel(-1)
notes_joined_with_keys = notes.join(keys_data, how="left",)
notes_by_keys_transposed = ms3.transpose_notes_to_localkey(notes_joined_with_keys)
tpc_distribution = notes_by_keys_transposed.reset_index(drop=True).groupby(['localkey_is_minor', 'tpc']).duration_qb.sum()
mode_tpcs = tpc_distribution.reset_index(-1).sort_values('tpc').reset_index()
mode_tpcs['sd'] = ms3.fifths2sd(mode_tpcs.tpc)
mode_tpcs['duration_pct'] = mode_tpcs.groupby('localkey_is_minor', group_keys=False).duration_qb.apply(lambda S: S / S.sum())
mode_tpcs['mode'] = mode_tpcs.localkey_is_minor.map({False: 'major', True: 'minor'})
corpuswise_tpc_distribution = notes_by_keys_transposed.groupby(["corpus", "localkey_is_minor", "tpc"]).duration_qb.sum().reset_index()
corpuswise_tpc_distribution['duration_pct'] = corpuswise_tpc_distribution.groupby(["corpus", "localkey_is_minor"], group_keys=False).duration_qb.apply(lambda S: S / S.sum())
std_err_mean = corpuswise_tpc_distribution.groupby(["localkey_is_minor", "tpc"]).duration_pct.sem().rename("std_err")
additional_columns = dict(
    sd = ms3.fifths2sd(mode_tpcs.tpc),
    duration_pct = mode_tpcs.groupby('localkey_is_minor', group_keys=False).duration_qb.apply(lambda S: S / S.sum()),
    mode = mode_tpcs.localkey_is_minor.map({False: 'major', True: 'minor'}),
    std_err = std_err_mean
)
mode_tpcs = mode_tpcs.join(std_err_mean, on=["localkey_is_minor", "tpc"])
mode_tpcs
localkey_is_minor tpc duration_qb sd duration_pct mode std_err
0 True -5 3.875000 b2 0.000375 minor NaN
1 True -4 517.995833 b6 0.050121 minor NaN
2 False -3 1.666667 b3 0.000158 major NaN
3 True -3 1358.205357 b3 0.131418 minor NaN
4 False -2 70.374993 b7 0.006674 major NaN
5 True -2 282.475000 b7 0.027332 minor NaN
6 False -1 1111.291603 4 0.105392 major NaN
7 True -1 1220.513690 4 0.118095 minor NaN
8 False 0 2518.249973 1 0.238824 major NaN
9 True 0 2325.180360 1 0.224981 minor NaN
10 False 1 2050.958283 5 0.194507 major NaN
11 True 1 1985.513690 5 0.192115 minor NaN
12 True 2 1432.580357 2 0.138614 minor NaN
13 False 2 1418.416620 2 0.134519 major NaN
14 True 3 236.892860 6 0.022921 minor NaN
15 False 3 759.458280 6 0.072025 major NaN
16 False 4 1553.124953 3 0.147294 major NaN
17 True 4 121.250000 3 0.011732 minor NaN
18 False 5 984.708297 7 0.093387 major NaN
19 True 5 836.017860 7 0.080892 minor NaN
20 True 6 14.500000 #4 0.001403 minor NaN
21 False 6 61.000000 #4 0.005785 major NaN
22 False 7 11.000000 #1 0.001043 major NaN
23 False 8 4.125000 #5 0.000391 major NaN
sd_order = ['b1', '1', '#1', 'b2', '2', '#2', 'b3', '3', '4', '#4', 'b5', '5', '#5', 'b6','6', '#6', 'b7', '7']
selector = (mode_tpcs.tpc > -8) & (mode_tpcs.tpc < 11)
legend=dict(
    yanchor="top",
    y=0.99,
    xanchor="right",
    x=0.99
)

fig = plotting.make_bar_plot(
    mode_tpcs[selector], #.query("duration_pct > 0.001")
    x_col='sd',
    y_col='duration_pct',
    #title="Scale degree distribution over major and minor segments",
    color='mode',
    barmode='group',
    color_discrete_map=utils.MAJOR_MINOR_COLORS,
    labels=dict(
        duration_pct='normalized duration',
        duration_qb="duration in ♩",
        sd="Notes transposed to the local key, as major-scale degrees",
        ),
    error_y="std_err",
    layout=dict(
        margin=dict(
            t=0
        )
    ),
    x_axis=dict(
        showgrid=False,
    ),
    #log_y=True,
    category_orders=dict(sd=sd_order)
    )
save_figure_as(fig, 'scale_degree_distributions_maj_min_normalized_bars', height=350, width=1200)
fig.show()
other_sum = mode_tpcs[~selector].sum()
print(f"{(~selector).sum()} scale degrees with a total duration of {other_sum.duration_qb} ♩ "
      f"({other_sum.duration_pct:.2%}) are not in the range -7 to 10 and have been omitted.")
0 scale degrees with a total duration of 0.0 ♩ (0.00%) are not in the range -7 to 10 and have been omitted.

Whole dataset#

chords_by_localkey = grouped_D.get_feature('HarmonyLabels')
chords_by_localkey
mc mn quarterbeats duration_qb mc_onset mn_onset timesig staff voice volta label pedal numeral form figbass changes relativeroot cadence phraseend chord_type chord_tones added_tones root bass_note alt_label globalkey_is_minor localkey_is_minor globalkey_mode localkey_mode localkey_resolved localkey_and_mode root_roman relativeroot_resolved effective_localkey effective_localkey_resolved effective_localkey_is_minor pedal_resolved chord_and_mode chord_reduced chord_reduced_and_mode applied_to_numeral numeral_or_applied_to_numeral intervals_over_bass intervals_over_root scale_degrees scale_degrees_and_mode scale_degrees_major scale_degrees_minor globalkey localkey chord
mode corpus piece localkey_slice i
major couperin_concerts c01n01_prelude [0.0, 16.0) 0 1 0 0 2.00 0 1/2 4/4 1 1 <NA> G.I{ <NA> I <NA> <NA> <NA> <NA> <NA> { M (0, 4, 1) () 0 0 <NA> False False major major I I, major I NaN I I False <NA> I, major I I, major <NA> I (M3, P5) (M3, P5) (1, 3, 5) (1, 3, 5), major (1, 3, 5) (1, #3, 5) G I I
1 2 1 2 2.00 0 0 4/4 1 1 <NA> V <NA> V <NA> <NA> <NA> <NA> <NA> <NA> M (1, 5, 2) () 1 1 <NA> False False major major I I, major V NaN I I False <NA> V, major V V, major <NA> V (M3, P5) (M3, P5) (5, 7, 2) (5, 7, 2), major (5, 7, 2) (5, #7, 2) G I V
2 2 1 4 0.50 1/2 1/2 4/4 1 1 <NA> I6 <NA> I <NA> 6 <NA> <NA> <NA> <NA> M (4, 1, 0) () 0 4 <NA> False False major major I I, major I NaN I I False <NA> I6, major I6 I6, major <NA> I (m3, m6) (M3, P5) (3, 5, 1) (3, 5, 1), major (3, 5, 1) (#3, 5, 1) G I I6
3 2 1 9/2 0.50 5/8 5/8 4/4 1 1 <NA> I <NA> I <NA> <NA> <NA> <NA> <NA> <NA> M (0, 4, 1) () 0 0 <NA> False False major major I I, major I NaN I I False <NA> I, major I I, major <NA> I (M3, P5) (M3, P5) (1, 3, 5) (1, 3, 5), major (1, 3, 5) (1, #3, 5) G I I
4 2 1 5 0.75 3/4 3/4 4/4 1 1 <NA> V(4) <NA> V <NA> <NA> 4 <NA> <NA> <NA> M (1, 0, 2) () 1 1 <NA> False False major major I I, major V NaN I I False <NA> V(4), major V V, major <NA> V (P4, P5) (P4, P5) (5, 1, 2) (5, 1, 2), major (5, 1, 2) (5, 1, 2) G I V(4)
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
minor couperin_concerts parnasse_07 [173.0, 212.0) 230 52 52 411/2 0.25 3/8 3/8 4/4 1 1 <NA> i64 <NA> i <NA> 64 <NA> <NA> <NA> <NA> m (1, 0, -3) () 0 1 <NA> True True minor minor i i, minor i NaN i i True <NA> i64, minor i64 i64, minor <NA> i (P4, m6) (m3, P5) (5, 1, 3) (5, 1, 3), minor (5, 1, b3) (5, 1, 3) b i i64
231 52 52 823/4 0.25 7/16 7/16 4/4 1 1 <NA> iio64 <NA> ii o 64 <NA> <NA> <NA> <NA> o (-4, 2, -1) () 2 -4 <NA> True True minor minor i i, minor ii NaN i i True <NA> iio64, minor iio64 iio64, minor <NA> ii (a4, M6) (m3, d5) (6, 2, 4) (6, 2, 4), minor (b6, 2, 4) (6, 2, 4) b i iio64
232 52 52 206 1.00 1/2 1/2 4/4 1 1 <NA> i6 <NA> i <NA> 6 <NA> <NA> <NA> <NA> m (-3, 1, 0) () 0 -3 <NA> True True minor minor i i, minor i NaN i i True <NA> i6, minor i6 i6, minor <NA> i (M3, M6) (m3, P5) (3, 5, 1) (3, 5, 1), minor (b3, 5, 1) (3, 5, 1) b i i6
233 52 52 207 1.00 3/4 3/4 4/4 1 1 <NA> V <NA> V <NA> <NA> <NA> <NA> <NA> <NA> M (1, 5, 2) () 1 1 <NA> True True minor minor i i, minor V NaN i i True <NA> V, minor V V, minor <NA> V (M3, P5) (M3, P5) (5, #7, 2) (5, #7, 2), minor (5, 7, 2) (5, #7, 2) b i V
234 53 53 208 4.00 0 0 4/4 1 1 <NA> i|PAC} <NA> i <NA> <NA> <NA> <NA> PAC } m (0, -3, 1) () 0 0 <NA> True True minor minor i i, minor i NaN i i True <NA> i, minor i i, minor <NA> i (m3, P5) (m3, P5) (1, 3, 5) (1, 3, 5), minor (1, b3, 5) (1, 3, 5) b i i

8376 rows × 51 columns

for is_minor, df in chords_by_localkey.groupby(level=0, group_keys=False):
    df = df.droplevel(0)
    df = df[df.bass_note.notna()]
    sd = ms3.fifths2sd(df.bass_note, minor=is_minor).rename('sd')
    sd.index = df.index
    sd_progression = df.groupby(level=[0,1,2], group_keys=False).bass_note.apply(lambda S: S.shift(-1) - S).rename('sd_progression')
    if is_minor == "minor":
        chords_by_localkey_minor = pd.concat([df, sd, sd_progression], axis=1)
    else:
        chords_by_localkey_major = pd.concat([df, sd, sd_progression], axis=1)

Scale degrees#

chords_by_localkey_minor
mc mn quarterbeats duration_qb mc_onset mn_onset timesig staff voice volta label pedal numeral form figbass changes relativeroot cadence phraseend chord_type chord_tones added_tones root bass_note alt_label globalkey_is_minor localkey_is_minor globalkey_mode localkey_mode localkey_resolved localkey_and_mode root_roman relativeroot_resolved effective_localkey effective_localkey_resolved effective_localkey_is_minor pedal_resolved chord_and_mode chord_reduced chord_reduced_and_mode applied_to_numeral numeral_or_applied_to_numeral intervals_over_bass intervals_over_root scale_degrees scale_degrees_and_mode scale_degrees_major scale_degrees_minor globalkey localkey chord sd sd_progression
corpus piece localkey_slice i
couperin_concerts c01n01_prelude [16.0, 22.5) 14 5 4 16 1.00 1/2 1/2 4/4 1 1 <NA> ii.iio{ <NA> ii o <NA> <NA> <NA> <NA> { o (2, -1, -4) () 2 2 <NA> False True major minor ii ii, major ii NaN ii ii True <NA> iio, minor iio iio, minor <NA> ii (m3, d5) (m3, d5) (2, 4, 6) (2, 4, 6), minor (2, 4, b6) (2, 4, 6) G ii iio 2 3
15 5 4 17 1.00 3/4 3/4 4/4 1 1 <NA> #viio7 <NA> #vii o 7 <NA> <NA> <NA> <NA> o7 (5, 2, -1, -4) () 5 5 <NA> False True major minor ii ii, major #vii NaN ii ii True <NA> #viio7, minor #viio7 #viio7, minor <NA> #vii (m3, d5, d7) (m3, d5, d7) (#7, 2, 4, 6) (#7, 2, 4, 6), minor (7, 2, 4, b6) (#7, 2, 4, 6) G ii #viio7 #7 -8
16 6 5 18 1.00 0 0 4/4 1 1 <NA> III+M7(9) <NA> III +M 7 9 <NA> <NA> <NA> +M7 (-3, 1, 5, 2) (-1,) -3 -3 <NA> False True major minor ii ii, major III NaN ii ii True <NA> III+M7(9), minor III+M7 III+M7, minor <NA> III (M3, a5, M7) (M3, a5, M7) (3, 5, #7, 2) (3, 5, #7, 2), minor (b3, 5, 7, 2) (3, 5, #7, 2) G ii III+M7(9) 3 0
17 6 5 19 1.00 1/4 1/4 4/4 1 1 <NA> i6 <NA> i <NA> 6 <NA> <NA> <NA> <NA> m (-3, 1, 0) () 0 -3 <NA> False True major minor ii ii, major i NaN ii ii True <NA> i6, minor i6 i6, minor <NA> i (M3, M6) (m3, P5) (3, 5, 1) (3, 5, 1), minor (b3, 5, 1) (3, 5, 1) G ii i6 3 2
18 6 5 20 1.00 1/2 1/2 4/4 1 1 <NA> ii65 <NA> ii <NA> 65 <NA> <NA> <NA> <NA> mm7 (-1, 3, 0, 2) () 2 -1 <NA> False True major minor ii ii, major ii NaN ii ii True <NA> ii65, minor ii65 ii65, minor <NA> ii (M3, P5, M6) (m3, P5, m7) (4, #6, 1, 2) (4, #6, 1, 2), minor (4, 6, 1, 2) (4, #6, 1, 2) G ii ii65 4 3
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
parnasse_07 [173.0, 212.0) 230 52 52 411/2 0.25 3/8 3/8 4/4 1 1 <NA> i64 <NA> i <NA> 64 <NA> <NA> <NA> <NA> m (1, 0, -3) () 0 1 <NA> True True minor minor i i, minor i NaN i i True <NA> i64, minor i64 i64, minor <NA> i (P4, m6) (m3, P5) (5, 1, 3) (5, 1, 3), minor (5, 1, b3) (5, 1, 3) b i i64 5 -5
231 52 52 823/4 0.25 7/16 7/16 4/4 1 1 <NA> iio64 <NA> ii o 64 <NA> <NA> <NA> <NA> o (-4, 2, -1) () 2 -4 <NA> True True minor minor i i, minor ii NaN i i True <NA> iio64, minor iio64 iio64, minor <NA> ii (a4, M6) (m3, d5) (6, 2, 4) (6, 2, 4), minor (b6, 2, 4) (6, 2, 4) b i iio64 6 1
232 52 52 206 1.00 1/2 1/2 4/4 1 1 <NA> i6 <NA> i <NA> 6 <NA> <NA> <NA> <NA> m (-3, 1, 0) () 0 -3 <NA> True True minor minor i i, minor i NaN i i True <NA> i6, minor i6 i6, minor <NA> i (M3, M6) (m3, P5) (3, 5, 1) (3, 5, 1), minor (b3, 5, 1) (3, 5, 1) b i i6 3 4
233 52 52 207 1.00 3/4 3/4 4/4 1 1 <NA> V <NA> V <NA> <NA> <NA> <NA> <NA> <NA> M (1, 5, 2) () 1 1 <NA> True True minor minor i i, minor V NaN i i True <NA> V, minor V V, minor <NA> V (M3, P5) (M3, P5) (5, #7, 2) (5, #7, 2), minor (5, 7, 2) (5, #7, 2) b i V 5 -1
234 53 53 208 4.00 0 0 4/4 1 1 <NA> i|PAC} <NA> i <NA> <NA> <NA> <NA> PAC } m (0, -3, 1) () 0 0 <NA> True True minor minor i i, minor i NaN i i True <NA> i, minor i i, minor <NA> i (m3, P5) (m3, P5) (1, 3, 5) (1, 3, 5), minor (1, b3, 5) (1, 3, 5) b i i 1 <NA>

4238 rows × 53 columns

fig = utils.make_sunburst(chords_by_localkey_major, parent='major', terminal_symbol=TERMINAL_SYMBOL)
fig.update_layout(**utils.STD_LAYOUT)
save_figure_as(fig, "bass_degree_major_sunburst")
fig.show()
fig = utils.make_sunburst(chords_by_localkey_minor, parent='minor', terminal_symbol=TERMINAL_SYMBOL)
fig.update_layout(**utils.STD_LAYOUT)
save_figure_as(fig, "bass_degree_minor_sunburst")
fig.show()
fig = utils.rectangular_sunburst(chords_by_localkey_major, path=['sd', 'figbass', 'interval'], title="MAJOR", terminal_symbol=TERMINAL_SYMBOL)
fig.update_layout(**utils.STD_LAYOUT)
save_figure_as(fig, "bass_degree-figbass-progression_major_sunburst")
fig.show()
fig = utils.rectangular_sunburst(chords_by_localkey_major, path=['sd', 'interval', 'figbass'], title="MAJOR", terminal_symbol=TERMINAL_SYMBOL)
fig.update_layout(**utils.STD_LAYOUT)
save_figure_as(fig, "bass_degree-progression-figbass_major_sunburst")
fig.show()
def make_table(sd_major, sd_progression_major, sd_minor=None, sd_progression_minor=None):
    selected_chords = chords_by_localkey_major[(
        (chords_by_localkey_major.sd == sd_major) &
        (chords_by_localkey_major.sd_progression == sd_progression_major)
    )]
    result = selected_chords.figbass.fillna('3').value_counts().rename(f"{sd_major} in major")
    if sd_minor is not None:
        selected_chords = chords_by_localkey_minor[(
            (chords_by_localkey_minor.sd == sd_minor) &
            (chords_by_localkey_minor.sd_progression == sd_progression_minor)
        )]
        minor_result = selected_chords.figbass.fillna('3').value_counts().rename(f"{sd_minor} in minor")
        result = pd.concat([result, minor_result], axis=1).fillna(0).astype(int)
    sum_row = pd.DataFrame(result.sum(), columns=["sum"]).T
    result = pd.concat([result, sum_row], names=["figbass"])
    return result

comparison_table = make_table("4", 5, "4", -2)
comparison_table #.to_clipboard()
4 in major 4 in minor
2 68 85
3 28 11
6 17 2
65 3 21
64 2 2
7 1 1
43 0 4
sum 119 126
selector = (
            (chords_by_localkey_minor.sd == "4") &
            (chords_by_localkey_minor.sd_progression == -2) &
            (chords_by_localkey_minor.figbass == "65")
        )
selector |= selector.shift()
selected_chords = chords_by_localkey_minor[selector]
selected_chords[["mn", "chord"]].droplevel([0, 2, 3]).to_clipboard()
fig = utils.rectangular_sunburst(chords_by_localkey_minor, path=['sd', 'figbass', 'interval'], title="MINOR", terminal_symbol=TERMINAL_SYMBOL)
fig.update_layout(**utils.STD_LAYOUT)
save_figure_as(fig, "bass_degree-figbass-progression_minor_sunburst")
fig.show()
fig = utils.rectangular_sunburst(chords_by_localkey_minor, path=['sd', 'interval', 'figbass'], title="MINOR", terminal_symbol=TERMINAL_SYMBOL)
fig.update_layout(**utils.STD_LAYOUT)
save_figure_as(fig, "bass_degree-progression-figbass_minor_sunburst")
fig.show()
fig = utils.rectangular_sunburst(chords_by_localkey_major, path=['sd', 'interval', 'figbass', 'following_figbass'], title="MAJOR", terminal_symbol=TERMINAL_SYMBOL)
fig.update_layout(**utils.STD_LAYOUT)
save_figure_as(fig, "bass_degree-progression-figbass-subsequent_figbass_major_sunburst")
fig.show()
fig = utils.rectangular_sunburst(chords_by_localkey_minor, path=['sd', 'interval', 'figbass', 'following_figbass'], title="MINOR", terminal_symbol=TERMINAL_SYMBOL)
fig.update_layout(**utils.STD_LAYOUT)
save_figure_as(fig, "bass_degree-progression-figbass-subsequent_figbass_minor_sunburst")
fig.show()